# -*- coding:utf-8 -*-
__author__ = 'xigu007'
__all__ = ('LabelContent', 'ImageContent', 'ShapeContent', 'ShapeProperty','WidgetLayout',)

from kivy.clock import Clock
from kivy.graphics import Rectangle, Color, RoundedRectangle, Ellipse, SmoothLine, Line
from kivy.core.text import Label
from kivy.metrics import sp
from kivy.uix.widget import Widget
from kivy.cache import Cache
from kivy.factory import Factory
from kivy.properties import ListProperty,NumericProperty
import os
from base import  Content, _appropriate_size

Cache_register = Cache.register
Cache_append = Cache.append
Cache_get = Cache.get
Cache_remove = Cache.remove
Cache_register('TextureLabel', timeout=60.)

class LabelContent(Content):
    def __init__(self, **kwargs):
        self.texture_size = (0,0)
        self._text_width_hint = None
        self._text_width = None
        super(LabelContent,self).__init__(**kwargs)
        self.label_options = {'font_size':sp(14)}
        #self.CoreLabel = Label(font_size=sp(14))
        self._vt_instruction = Rectangle(pos = (0,0),size=(0,0))
        if kwargs.has_key('color'):
            self.label_options.update({'color':kwargs['color']})
        self.label_options.update({'text':kwargs.get('text', '')})
        if kwargs.has_key('font_size'):
            #self.font_size = kwargs['font_size']
            self.label_options.update({'font_size':kwargs['font_size']})
        if kwargs.has_key('shorten'):
            #self._shorten = kwargs['shorten']
            self.label_options.update({'shorten':kwargs['shorten']})
        if kwargs.has_key('line_height'):
            #self._line_height = kwargs['line_height']
            self.label_options.update({'line_height':kwargs['line_height']})
        font_name = kwargs.get('font_name', os.environ['KIVY_VALID_FONT'])
        self.label_options.update({'font_name':font_name})

    def _update_label(self,dt):
        if self.property_changed:
            self._rend_text()
            self.property_changed = False

    def layout_size(self, ref_size):
        if self._is_relayout(ref_size):
            self.property_changed = True
        if self.property_changed:
            self._rend_text()
            self.property_changed = False
        return self._size

    def _rend_text(self):
        if self._text_width_hint is not None:
            text_size = (self._text_width_hint*(self._ref_size[0] - self._margin[0] - self._margin[1]), None)
        elif self._text_width is not None:
            text_size = (self._text_width, None)
        else:
            text_size = (None, None)
        kw = self.label_options
        kw_text_size = kw.get('text_size',None)
        if kw_text_size is None:
            kw['text_size'] = text_size

        label = None
        kw_text_size = kw.get('text_size',None)
        if kw_text_size is not None:
            text = kw['text']
            text_size = kw['text_size'][0]
            if isinstance(text, unicode):
                tx_key = u'{0}\{1}\{2}\{3}'.format(text, kw.get('font_size', u''),kw.get('color', u''),text_size)
            else:
                tx_key = '{0}\{1}\{2}\{3}'.format(text, kw.get('font_size', ''),kw.get('color', ''),text_size)
            label = Cache_get('TextureLabel', tx_key)

        if label is None:
            label = Label(**kw)
            try:
                label.fast_render('中')
            except:
                label.refresh()
            if self._max_width is not None:
                print 'label re render'
                if label.texture.size[0] > self._max_width:
                    label.text_size = (self._max_width, None)
                    label.options['text_size'] = label.text_size
                    try:
                        label.fast_render('中')
                    except:
                        label.refresh()
            if kw_text_size is not None:
                Cache_append('TextureLabel', tx_key, label)
        #else:
        #    print 'cach label------'
        texture_size = label.texture.size
        self.texture_size = texture_size[:]
        self._vt_instruction.texture = label.texture
        self._size = (self.texture_size[0] + self._margin[0] + self._margin[1], self.texture_size[1] + self._margin[2] + self._margin[3])

    @property
    def text(self):
        return self.label_options['text']

    @text.setter
    def text(self,value):
        if value != self.label_options['text']:
            self.label_options['text']= value
            self.notify_property_changed()

    @property
    def color(self):
        if self.label_options.has_key('color'):
            return self.label_options['color']
        return (1,1,1,1)
    @color.setter
    def color(self,value):
        changed = False
        if self.label_options.has_key('color'):
            if value != self.label_options['color']:
                changed = True
        else:
            changed = True
        if changed:
            self.label_options['color'] = value
            self.notify_property_changed()
    @property
    def shorten(self):
        if self.label_options.has_key('shorten'):
            return self.label_options['shorten']
        else:
            False

    @shorten.setter
    def shorten(self,value):
        changed = False
        if self.label_options.has_key('shorten'):
            if value != self.label_options['shorten']:
                changed = True
        else:
            changed = True
        if changed:
            self.label_options['shorten'] = value
            self.label_options['shorten_from'] = 'right'
            self.notify_property_changed()

    @property
    def line_height(self):
        if self.label_options.has_key('line_height'):
            return self.label_options['line_height']
        else:
            return 1.0

    @line_height.setter
    def line_height(self,value):
        changed = False
        if self.label_options.has_key('line_height'):
            if value != self.label_options['line_height']:
                changed = True
        else:
            changed = True
        if changed:
            self.label_options['line_height'] = value
            self.notify_property_changed()

    @property
    def font_size(self):
        return self.label_options['font_size']

    @font_size.setter
    def font_size(self,value):
        if value != self.label_options['font_size']:
            self.label_options['font_size'] = value
            self.notify_property_changed()

    @property
    def font_name(self):
        return self.label_options['font_name']

    @font_name.setter
    def font_name(self,value):
        if value != self.label_options['font_name']:
            self.label_options['font_name'] = value
            self.notify_property_changed()

    @property
    def text_width(self):
        return self._text_width

    @text_width.setter
    def text_width(self,value):
        if value != self._text_width:
            self._text_width = value
            self.notify_property_changed()

    @property
    def text_width_hint(self):
        return self._text_width_hint

    @text_width_hint.setter
    def text_width_hint(self, value):
        if value != self._text_width_hint:
            self._text_width_hint = value
            self.notify_property_changed()

class ImageContent(Content):
    def __init__(self, **kwargs):
        self._keep_ratio = True
        self._scale_able = True
        self._cur_size = None
        self._source = ''
        self.texture_size = None
        self._ratio = 1.0
        self._image_size = None
        super(ImageContent,self).__init__(**kwargs)
        self._vt_instruction = Rectangle(pos = (0,0),size=(0,0))

    def layout_size(self, ref_size):
        if self._is_relayout(ref_size):
            self.property_changed = True
        if not self.property_changed:
            return self._size
        margin = self._margin
        w, h  =  max(ref_size[0] - margin[0] - margin[1], 0), max(ref_size[1] - margin[2] - margin[3], 0)
        if self.texture_size:
            hint_w = None
            hint_h = None
            image_w, image_h = w, h
            if not self._scale_able:
                image_w, image_h = self.texture_size[:]
            else:
                if image_h == 0:
                    #未设置div高度按texture实际大小显示
                    image_w, image_h = self.texture_size[:]
                if self._image_size is not None:
                    _w, _h = self._image_size
                    if _w is not None:
                        image_w = _w
                    if _h is not None:
                        image_h = _h
                else:
                    if self._size_hint is not None:
                        hint_w, hint_h = self._size_hint
                    if hint_w is not None :
                        image_w = hint_w*w
                    if hint_h is not None :
                        image_h = hint_h*h
                if self._keep_ratio:
                    ratio = self._ratio
                    tw = image_w
                    th = tw / ratio
                    if image_h is not None and th > image_h:
                        th = image_h
                        tw = th*ratio
                    image_w = tw
                    image_h = th
            if self._min_height is not None:
                if image_h < self._min_height:
                    image_h = self._min_height
                    if self._keep_ratio:
                        image_w = image_h*self._ratio
            if self._min_width is not None:
                if image_w < self._min_width:
                    image_w = self._min_width
                    if self._keep_ratio:
                        image_h = image_w / self._ratio
            if self._max_width is not None:
                if image_w > self._max_width:
                    image_w = self._max_width
                    if self._keep_ratio:
                        image_h = image_w / self._ratio
            if self._max_height is not None:
                if image_h > self._max_height:
                    image_h = self._max_height
                    if self._keep_ratio:
                        image_w = image_h*self._ratio
            self.property_changed = False
            self._size = (image_w + margin[0] + margin[1], image_h + margin[2] + margin[3])
        return self._size

    @property
    def source(self):
        return self._source

    @source.setter
    def source(self, value):
        if value != self._source:
            self._source = value
            self._vt_instruction.source = self._source
            self.texture_size = self._vt_instruction.texture.size
            if self._keep_ratio:
                texture_w, texture_h = self.texture_size
                ratio = float(texture_w) / texture_h
                if self._size[0] < 1.0 or self._size[1] < 1.0:
                    self.notify_property_changed()
                elif abs(self._ratio - ratio) > 0.01:
                    self.notify_property_changed()
                self._ratio = ratio

    @property
    def keep_ratio(self):
        return self._keep_ratio

    @keep_ratio.setter
    def keep_ratio(self, value):
        if self._keep_ratio != value:
            self._keep_ratio = value
            self.notify_property_changed()

    @property
    def scale_able(self):
        return self._scale_able

    @scale_able.setter
    def scale_able(self, value):
        if self._scale_able != value:
            self._scale_able = value
            self.notify_property_changed()

    @property
    def image_size(self):
        return self._image_size

    @image_size.setter
    def image_size(self, value):
        if self._image_size != value:
            self._image_size = value
            self.notify_property_changed()


class ShapeProperty(object):
    def __init__(self,**kwargs):
        self._solid = kwargs.get('solid', True)
        self._ellipse = kwargs.get('ellipse', False)
        self._rounded_flg = kwargs.get('rounded_flg', False)
        self._image_source = kwargs.get('image_source', '')
        self._line_width = kwargs.get('line_width', 1)
        self._radius = kwargs.get('radius', 10.0)
        self._rebuild_trigger = None
        super(ShapeProperty, self).__init__()

    def _trigger_rebuild(self, dt):
        instruction = self._instruction_group
        if (instruction is not None) and (self._vt_instruction is not None):
            old_instruction = self._vt_instruction
            old_index = instruction.indexof(self._vt_instruction)
            if old_index > 0:
                instruction.remove(old_instruction)
            self.build_instruction()
            instruction.add(self._vt_instruction)

    def build_instruction(self):
        #print '---build_instruction------'
        if self.solid:
            if self._ellipse:
                self._vt_instruction = Ellipse(pos=self.get_content_real_pos(), size=self.get_content_real_size())
            elif self._rounded_flg:
                self._vt_instruction = RoundedRectangle(pos=self.get_content_real_pos(), size=self.get_content_real_size(), radius=[self._radius])
            else:
                self._vt_instruction = Rectangle(pos=self.get_content_real_pos(), size=self.get_content_real_size())
            if self._image_source:
                self._vt_instruction.source = self._image_source
        else:
            x, y = self.get_content_real_pos()
            w, h = self.get_content_real_size()
            #print 'shape pos={} size={}'.format((x,y),(w,h))
            if self._ellipse:
                self._vt_instruction = SmoothLine(width=self._line_width)
                self._vt_instruction.ellipse = (x,y,w,h, 0, 360)
            elif self._rounded_flg:
                self._vt_instruction = SmoothLine(width=self._line_width)
                self._vt_instruction.rounded_rectangle =(x, y, w, h, self._radius)
            else:
                self._vt_instruction = Line(width=self._line_width)
                self._vt_instruction.rectangle =(x, y, w, h)

    def change_property(self):
        if self._solid:
            self._vt_instruction.size = self.get_content_real_size()
            self._vt_instruction.pos = self.get_content_real_pos()
        else:
            x, y = self.get_content_real_pos()
            w, h = self.get_content_real_size()
            if self._ellipse:
                self._vt_instruction.ellipse = (x,y,w,h, 0,360,40)
            elif self._rounded_flg:
                self._vt_instruction.rounded_rectangle = (x, y, w, h, self._radius, 100)
            else:
                self._vt_instruction.rectangle = (x, y, w, h)

    def _rebuild(self):
        if self._vt_instruction is not None:
            if self._rebuild_trigger is None:
                self._rebuild_trigger = Clock.create_trigger(self._trigger_rebuild)
            self._rebuild_trigger()

    def get_content_real_size(self):
        pass

    def get_content_real_pos(self):
        pass

    @property
    def solid(self):
        return self._solid

    @solid.setter
    def solid(self, value):
        if self._solid != value:
            self._solid = value
            self._rebuild()

    @property
    def ellipse(self):
        return self._ellipse

    @ellipse.setter
    def ellipse(self, value):
        if self._ellipse != value:
            self._ellipse = value
            if self._ellipse:
                self._rebuild()

    @property
    def image_source(self):
        return self._image_source

    @image_source.setter
    def image_source(self, value):
        if self._solid:
            if self._image_source != value:
                self._image_source = value
                if self._vt_instruction is not None:
                    if self._image_source:
                        self._vt_instruction.source = self._image_source
    @property
    def line_width(self):
        return self._line_width

    @line_width.setter
    def line_width(self, value):
        if self._solid:
            return
        self._line_width = value
        if self._vt_instruction is not None:
            self.change_property()

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if (value is None) or (not value):
            self._rounded_flg = False
            return
        self._radius = value
        if self._ellipse:
            return
        if self._vt_instruction is not None:
            if not self._rounded_flg:
                self._rounded_flg = True
                self._rebuild()
            else:
                self.change_property()
        else:
            self._rounded_flg = True

class ShapeContent(Content, ShapeProperty):
    def __init__(self, **kwargs):
        self._shape_size = kwargs.get('shape_size',None)
        self._aspect_ratio = kwargs.get('aspect_ratio',1.0)
        super(ShapeContent, self).__init__(**kwargs)
        if kwargs.has_key('color'):
            self._clear_color_state = True
            self._color = kwargs['color']

    def do_apply(self):
        if self._vt_instruction :
            self.change_property()

    def layout_size(self, ref_size):
        if self._is_relayout(ref_size):
            self.property_changed = True
        if not self.property_changed:
            #print 'property not changed'
            return self._size
        margin = self._margin
        act_size  =  max(ref_size[0] - margin[0] - margin[1], 0), max(ref_size[1] - margin[2] - margin[3], 0)
        shape_w, shape_h = _appropriate_size(act_size, self._shape_size, self._size_hint, self._aspect_ratio, self._max_width, self._min_width, self._max_height, self._min_height)
        self._size = (shape_w + margin[0] + margin[1], shape_h + margin[2] + margin[3])
        self.property_changed = False
        return self._size

    def get_content_real_size(self):
        margin = self._margin
        return (self._size[0] - margin[0] - margin[1], self._size[1] - margin[2] - margin[3])

    def get_content_real_pos(self):
        margin = self._margin
        return self._x + margin[0], self._y +  margin[3]

    def add_instruction(self):
        if self._vt_instruction is None:
            self.build_instruction()
        super(ShapeContent, self).add_instruction()


    @property
    def aspect_ratio(self):
        return self._aspect_ratio

    @aspect_ratio.setter
    def aspect_ratio(self, value):
        #宽高比
        if self._aspect_ratio != value:
            self._aspect_ratio = value
            self.notify_property_changed()

    @property
    def shape_size(self):
        return self._shape_size

    @shape_size.setter
    def shape_size(self, value):
        if self._shape_size != value:
            self._shape_size = value
            self.notify_property_changed()

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value):
        self._color = value
        self._clear_color_state = True
        if self._color_instruction:
            self._color_instruction.rgba = self._color

class WidgetLayout(Content):
    '''
    把kivy的widget普通控件当作content嵌入到compositewidget里。
    '''
    def __init__(self,**kwargs):
        self._wiget_size = None
        self._aspect_ratio = 1.0
        self._ref_size = (0,0)
        self._widget = None
        super(WidgetLayout, self).__init__(**kwargs)

    def set_widget(self,widget):
        self._widget = widget
        if widget:
            self.notify_property_changed()
    def do_apply(self):
        widget = self._widget
        if widget:
            widget.size = (self._size[0] - self._margin[0] - self._margin[1], self._size[1] - self._margin[2] - self._margin[3])
            widget.pos = (self._x + self._margin[0], self._y + self._margin[3])

    def layout_size(self, ref_size):
        if self._is_relayout(ref_size):
            self.property_changed = True
        if not self.property_changed:
            #print 'property not changed'
            return self._size
        margin = self._margin
        act_size  =  max(ref_size[0] - margin[0] - margin[1], 0), max(ref_size[1] - margin[2] - margin[3], 0)
        wiget_w, wiget_h = _appropriate_size(act_size, self._wiget_size, self._size_hint, self._aspect_ratio, self._max_width, self._min_width, self._max_height, self._min_height)
        self._size = (wiget_w + margin[0] + margin[1], wiget_h + margin[2] + margin[3])
        self.property_changed = False
        return self._size

    def add_instruction(self):
        widget = self._widget
        instruction_group = self._instruction_group
        added_flg = self.push_state(instruction_group)
        if not added_flg:
            if widget:
                instruction_group.add(widget.canvas)
                self._added_flg = True
        self.pop_state(instruction_group)

    @property
    def aspect_ratio(self):
        return self._aspect_ratio

    @aspect_ratio.setter
    def aspect_ratio(self, value):
        #宽高比
        if self._aspect_ratio != value:
            self._aspect_ratio = value
            self.notify_property_changed()

    @property
    def widget_size(self):
        return self._widget_size

    @widget_size.setter
    def widget_size(self, value):
        if self._widget_size != value:
            self._widget_size = value
            self.notify_property_changed()